import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rcParams
import matplotlib.font_manager as fm
from sklearn.preprocessing import StandardScaler

# 一、Matplotlib字体与显示设置
# 定位SimHei字体在Windows系统中的存储路径，若换系统需调整
font_path = 'C:/Windows/Fonts/simhei.ttf'
# 依据字体路径生成可供Matplotlib使用的字体属性实例
prop = fm.FontProperties(fname=font_path)
# 全局指定Matplotlib绘图使用的字体为SimHei，保障中文文本正常呈现
rcParams['font.family'] = prop.get_name()
# 修正负号在图形中可能出现的乱码显示问题，确保正常显示
rcParams['axes.unicode_minus'] = False

# 二、数据读取与准备
# 数据源链接，指向知名的UCI葡萄酒数据集
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data"
# 详细定义数据集各列名称，涵盖分类标识与多项化学指标
columns = ['Class', 'Alcohol', 'Malic_acid', 'Ash', 'Alcalinity_of_ash', 'Magnesium',
           'Total_phenols', 'Flavanoids', 'Nonflavanoid_phenols', 'Proanthocyanins',
           'Color_intensity', 'H320_OD315', 'OD280/OD315', 'Proline']
# 借助pandas库从CSV格式数据源加载数据，指定无原始标题，按预设列名解析
data = pd.read_csv(url, header=None, names=columns)

# 三、数据筛选与特征提取
# 从原始数据中挑出类别为1和2的样本子集，用于聚焦分析
filtered_data = data[data['Class'].isin([1, 2])]
# 分离出特征矩阵，排除类别列，为后续机器学习模型输入做准备
X = filtered_data.iloc[:, 1:]

# 四、数据标准化处理
# 实例化标准化工具，它将依据数据自身统计特性进行转换
scaler = StandardScaler()
# 对特征数据执行标准化操作，使其符合标准正态分布特征，利于模型训练
X_scaled = scaler.fit_transform(X)


# 五、K-Means聚类算法主体函数
def kmeans(X, k=2, max_iters=100):
    """
    实现K-Means聚类算法，将数据划分为指定数量的簇。

    参数：
    X (numpy.ndarray): 二维数组，代表待聚类的样本特征，每行一个样本，每列一个特征。
    k (int, 可选): 期望划分的簇数量，默认值为2。
    max_iters (int, 可选): 算法允许的最大迭代轮次，防止陷入死循环，默认100。

    返回：
    centroids (numpy.ndarray): 最终收敛的聚类中心坐标，二维数组，形状为(k, 特征数)。
    labels (numpy.ndarray): 一维数组，记录每个样本所属的簇标签。
    """
    # 随机抽取k个样本作为初始聚类中心，确保起点多样性
    centroids = X[np.random.choice(X.shape[0], k, replace=False)]
    prev_centroids = np.zeros_like(centroids)
    labels = np.zeros(X.shape[0])

    # 开启迭代优化过程，直至满足收敛条件或达到最大迭代次数
    for _ in range(max_iters):
        # 1. 样本归类步骤
        for i in range(X.shape[0]):
            # 度量样本到各聚类中心的欧氏距离，为归类提供依据
            distances = np.linalg.norm(X[i] - centroids, axis=1)
            # 依据距离最小原则，为样本分配簇标签
            labels[i] = np.argmin(distances)

        # 2. 聚类中心更新步骤
        for i in range(k):
            # 基于同簇样本，重新计算聚类中心位置，取均值作为新中心
            centroids[i] = X[labels == i].mean(axis=0)

        # 检查聚类中心是否稳定，若已稳定，提前终止迭代
        if np.all(centroids == prev_centroids):
            break
        prev_centroids = centroids.copy()

    return centroids, labels


# 六、执行K-Means聚类并可视化结果
centroids, labels = kmeans(X_scaled, k=2)

plt.figure(figsize=(8, 6))

# 绘制散点图呈现聚类结果，依据标签上色，展示样本分布态势
plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=labels, cmap='viridis', edgecolor='k', s=50)

# 突出显示聚类中心，以红色X标记，便于识别
plt.scatter(centroids[:, 0], centroids[:, 1], c='red', marker='X', s=200, label='聚类中心')

# 为图表添加清晰标题与坐标轴标签，点明主旨
plt.title('K-Means 聚类结果')
plt.xlabel('酒精含量 (Alcohol)')
plt.ylabel('苹果酸含量 (Malic acid)')

# 添加上传说明性图例，辅助理解图表构成
plt.legend()

# 调出绘制完成的图形窗口，展示可视化成果
plt.show()